#include "ModbusHost.h"

#include "crc.h"
#include "FreeRTOS.h"
#include "task.h"

/********************************************************************
modbus RTU 的C51程序 
单片机AT89C51          11.0592MHZ
通信波特率 9600 8位数据 1位停止位  485通位接口
单片机控制板地址 localAddr(变量)
 0x01 0x03 0x00 0x00 0x01 0x84 0x0a
**********************************************************************/
/************************变量数据修改替换区，移植时可按以下变量模板替换即可****************/
 uint16_t ROdat[3]={0,0,0}; 	 //3个只读整型
 uint16_t RWdat[6]={0,0,0,0,0,0}; 	 //3个可读可写整型
 float temp=23.4;									 //一个浮点型
 long   adc=789654;									//一个long型
 int    ROstatus0=0;                              //只读线圈
 int    ROstatus1=1;                              //只读线圈
 int    RWstatus0=0;                              //可读可写线圈
 int    RWstatus1=1;                              //可读可写线圈


/*************针对占两个寄存器的浮点型和长整型定义的结构体**********************************/
union uf		  //浮点型数据	占两个寄存器
{
	uint16_t x[2];
	float y;
}ftemp;

union ul			   //长整型数据	占两个寄存器
{
uint16_t x[2];
long y;
}ltemp;
/***********************************************************************/
uint8_t receCount;    //接收到的字节个数

uint16_t TimeOutCount = 1000;



 static uint16_t setRegisterVal(uint16_t addr,uint16_t tempData);
 static uint16_t getRegisterVal(uint16_t addr,uint16_t *tempData);
 static uint16_t setCoilVal(uint16_t addr,uint16_t tempData);
 static uint16_t getCoilVal(uint16_t addr,uint16_t *tempData);






uint8_t uartsends(int sockfd,uint8_t buff[],uint8_t len)
{
	usart2_rcv.rcv_len = 0;
	uart2_sends((char*)buff,len);
	return 0;
}

uint8_t uartrcv(int sockfd,uint8_t buff[],uint8_t len,int timeOutCount)
{
	int time_now = timeOutCount/100; 
	while(usart2_rcv.rcv_len != len)
	{
	//	printf("usart2_rcv.rcv_len %d\r\n",usart2_rcv.rcv_len);
		vTaskDelay(100);
		time_now--;
		if(time_now < 0)
			return 1;
	}
	memcpy(buff,usart2_rcv.rcv_buff,usart2_rcv.rcv_len);
	return 0;
}

/***************************** 以下代码一直到数据交换区为固定函数，移植时不用管，不用修改*************************************************/	



//fuction:01 读单个或多个线圈状态 addr:从机地址 registerStartaddr：寄存器起始地址  bitCount寄存器个数
uint8_t readCoils(int sockfd,uint8_t addr,uint16_t registerStartaddr,uint16_t bitCount)
{
	uint16_t byteCount,temp;
	uint16_t recbyteCount;  //接收字节个数
	uint16_t crcData;
	uint16_t  address; //当前寄存器地址
	uint16_t  i,k;
	uint16_t tempData;
	uint8_t  exit = 0;
	uint8_t receBuf[50];
	uint8_t sendBuf[50];


	sendBuf[0]=addr;
	sendBuf[1]=1;
	sendBuf[2]=registerStartaddr>>8;
	sendBuf[3]=registerStartaddr;
	sendBuf[4]=bitCount>>8;
	sendBuf[5]=bitCount;
	crcData = crc16(sendBuf,6);
	sendBuf[6] = crcData & 0xff;   // CRC代码低位在前
    sendBuf[7] = crcData >> 8 ;	   //高位在后	 
	receCount = 0; 	 //清接收地址偏移寄存器, 等待接收

	if(uartsends(sockfd,sendBuf,8))
	return FAILURE;   

    byteCount = bitCount / 8;    //计算接收字节个数
	if(bitCount%8 != 0)
    byteCount++; 
    recbyteCount=byteCount+5;
//	TimeOutCount=recbyteCount*4; //根据接收数据长度确定收接超时时间

	if(uartrcv(sockfd,receBuf,recbyteCount,TimeOutCount))
	return FAILURE;                          

 	if((receBuf[0]==addr)&&(receBuf[1]==1))   //核对接收数据地址
    {	 
	  address=registerStartaddr;
      crcData = crc16(receBuf,receBuf[2]+3);                     //核对校验码
	  temp=receBuf[receBuf[2]+4];
	  temp=(temp<<8)+receBuf[receBuf[2]+3];			 //低位在前
      if(crcData == temp)
        {
 		for(k=0;k<byteCount;k++)//字节位置
  			{
    		for(i=0;i<8;i++)
    			{
     			 tempData = (receBuf[k+3] >>i)&0x01;  
      			 setCoilVal(address,tempData); 
      			 address++;
      		    if(address >= registerStartaddr+bitCount)//读完
      		       { 
        		    exit = 1;
            		break;
                   } 
    			}
  		 if(exit == 1)
  		  break;
  			}		  
		  	receCount = 0; 
		  return SUCCESS;
		}									  
      }	
	  receCount = 0; 
 return FAILURE;
}

//fuction:02 读输入线圈状态 addr:从机地址 registerStartaddr：寄存器起始地址  bitCount寄存器个数
uint8_t readInputCoils(int sockfd,uint8_t addr,uint16_t registerStartaddr,uint16_t bitCount)
{
	uint16_t byteCount,temp;
	uint16_t recbyteCount;  //接收字节个数
	uint16_t crcData;
	uint16_t  address; //当前寄存器地址
	uint16_t  i,k;
	uint16_t tempData;
	uint8_t  exit = 0;
	uint8_t receBuf[50];
	uint8_t sendBuf[50];

	sendBuf[0]=addr;
	sendBuf[1]=2;
	sendBuf[2]=registerStartaddr>>8;
	sendBuf[3]=registerStartaddr;
	sendBuf[4]=bitCount>>8;
	sendBuf[5]=bitCount;
	crcData = crc16(sendBuf,6);
	sendBuf[6] = crcData & 0xff;   // CRC代码低位在前
    sendBuf[7] = crcData >> 8 ;	   //高位在后   	 
	receCount = 0; 	 //清接收地址偏移寄存器, 等待接收

	if(uartsends(sockfd,sendBuf,8))
	return FAILURE;  

    byteCount = bitCount / 8;    //计算接收字节个数
	if(bitCount%8 != 0)
    byteCount++; 
    recbyteCount=byteCount+5;
//	TimeOutCount=recbyteCount*4; //根据接收数据长度确定收接超时时间

	if(uartrcv(sockfd,receBuf,recbyteCount,TimeOutCount))
	return FAILURE;                          //关中断

 	if((receBuf[0]==addr)&&(receBuf[1]==1))   //核对接收数据地址
      {	 
	  address=registerStartaddr;
      crcData = crc16(receBuf,receBuf[2]+3);                     //核对校验码
	  temp=receBuf[receBuf[2]+4];
	  temp=(temp<<8)+receBuf[receBuf[2]+3];			 //低位在前
      if(crcData == temp)
        {
 		for(k=0;k<byteCount;k++)//字节位置
  			{
    		for(i=0;i<8;i++)
    			{
     			 tempData = (receBuf[k+3] >>i)&0x01;  
      			 setCoilVal(address+10000,tempData); 
      			 address++;
      		    if(address >= registerStartaddr+bitCount)//读完
      		       { 
        		    exit = 1;
            		break;
                   } 
    			}
  		 if(exit == 1)
  		  break;
  			}		  
		  	receCount = 0; 
		  return SUCCESS;
		}									  
      }	
	  receCount = 0; 
 return FAILURE;
}


			
/*******************************fuction:03读寄存器  ********************************/
                         //addr:从机地址   registerStartaddr：寄存器起始地址   registernum：读取寄存器数量
uint8_t readHoldRegisters(int sockfd,uint8_t addr,uint16_t registerStartaddr,uint16_t registernum)
{
	uint16_t crcData,temp,i;
	uint16_t recbyteCount;  //接收字节个数
	uint16_t tempData;
	uint8_t receBuf[50];
	uint8_t sendBuf[50];

	sendBuf[0]=addr;
	sendBuf[1]=3;
	sendBuf[2]=registerStartaddr>>8;
	sendBuf[3]=registerStartaddr;
	sendBuf[4]=registernum>>8;
	sendBuf[5]=registernum;
	crcData = crc16(sendBuf,6);
	sendBuf[6] = crcData & 0xff;   // CRC代码低位在前
    sendBuf[7] = crcData >> 8 ;	   //高位在后	 
	receCount = 0; 	 //清接收地址偏移寄存器, 等待接收

	if(uartsends(sockfd,sendBuf,8))
	return FAILURE; 

	recbyteCount=registernum*2+5;
//	TimeOutCount=recbyteCount*4; //根据接收数据长度确定收接超时时间

	if(uartrcv(sockfd,receBuf,recbyteCount,TimeOutCount))
	return FAILURE;

	 if((receBuf[0]==addr)&&(receBuf[1]==3))   //核对接收数据地址
      {	 
      crcData = crc16(receBuf,receBuf[2]+3);                     //核对校验码
	  temp=receBuf[receBuf[2]+4];
	  temp=(temp<<8)+receBuf[receBuf[2]+3];			 //低位在前
      if(crcData == temp)
        {
		  for(i=0;i<registernum;i++)
		  {
		 	tempData=receBuf[3+2*i]<<8;
		 	tempData+=receBuf[4+2*i];
		  	setRegisterVal(registerStartaddr+i+40000,tempData);	  //+40000偏移地址
		  }
		  	receCount = 0; 
		  return SUCCESS;
		}									  
      }	
	  receCount = 0; 
	 return FAILURE;
}

/*******************************fuction:04读输入寄存器  ********************************/
                         //addr:从机地址   registerStartaddr：寄存器起始地址   registernum：读取寄存器数量
uint8_t readInputRegisters(int sockfd,uint8_t addr,uint16_t registerStartaddr,uint16_t registernum)
{
	uint16_t crcData,temp,i;
	uint16_t recbyteCount;  //接收字节个数
	uint16_t tempData;
	uint8_t receBuf[50];
	uint8_t sendBuf[50];

	sendBuf[0]=addr;
	sendBuf[1]=4;
	sendBuf[2]=registerStartaddr>>8;
	sendBuf[3]=registerStartaddr;
	sendBuf[4]=registernum>>8;
	sendBuf[5]=registernum;
	crcData = crc16(sendBuf,6);
	sendBuf[6] = crcData & 0xff;   // CRC代码低位在前
    sendBuf[7] = crcData >> 8 ;	   //高位在后	 
	receCount = 0; 	 //清接收地址偏移寄存器, 等待接收

	if(uartsends(sockfd,sendBuf,8))
	return FAILURE; 

	recbyteCount=registernum*2+5;
//	TimeOutCount=recbyteCount*4; //根据接收数据长度确定收接超时时间

	if(uartrcv(sockfd,receBuf,recbyteCount,TimeOutCount))
	return FAILURE;

	 if((receBuf[0]==addr)&&(receBuf[1]==3))   //核对接收数据地址
      {	 
      crcData = crc16(receBuf,receBuf[2]+3);                     //核对校验码
	  temp=receBuf[receBuf[2]+4];
	  temp=(temp<<8)+receBuf[receBuf[2]+3];			 //低位在前
      if(crcData == temp)
        {
		  for(i=0;i<registernum;i++)
		  {
		 	tempData=receBuf[3+2*i]<<8;
		 	tempData+=receBuf[4+2*i];
		  	setRegisterVal(registerStartaddr+i+30000,tempData);	  //+30000偏移地址
		  }
		  	receCount = 0; 
		  return SUCCESS;
		}									  
      }	
	  receCount = 0; 
	 return FAILURE;
}

   /***********************设置单片线圈值fuction:05****************************/
  // addr:从机地址,registeraddr：寄存器地址,tempData ：欲设置的值(限0x00 ,0xff00)  
uint8_t presetSingleCoil(int sockfd,uint8_t addr,uint16_t registeraddr,uint16_t tempData)
{
	uint16_t crcData;
	uint8_t receBuf[50];
	uint8_t sendBuf[50];

	sendBuf[0]=addr;
	sendBuf[1]=5;
	sendBuf[2]=registeraddr>>8;
	sendBuf[3]=registeraddr;
	sendBuf[4]=tempData>>8;
	sendBuf[5]=tempData;
	crcData = crc16(sendBuf,6);
	sendBuf[6] = crcData & 0xff;   // CRC代码低位在前
    sendBuf[7] = crcData >> 8 ;	   //高位在后
	receCount = 0; 

	if(uartsends(sockfd,sendBuf,8))
	return FAILURE; 

	if(uartrcv(sockfd,receBuf,8,TimeOutCount))
	return FAILURE;                          

      if((receBuf[0]==addr)&&(receBuf[1]==5)&&(receBuf[6]==sendBuf[6])&&(receBuf[7]==sendBuf[7]))
        {
		receCount = 0;
		  return SUCCESS;
		}									        
	  receCount = 0;
	  return FAILURE;
}


  /***********************设置单片寄存器值fuction:06****************************/
  // addr:从机地址,registeraddr：寄存器地址,tempData ：欲设置的值
uint8_t presetSingleRegister(int sockfd,uint8_t addr,uint16_t registeraddr,uint16_t tempData)
{
   	uint16_t crcData;
	uint8_t receBuf[50];
	uint8_t sendBuf[50];
	sendBuf[0]=addr;
	sendBuf[1]=6;
	sendBuf[2]=registeraddr>>8;
	sendBuf[3]=registeraddr;
	sendBuf[4]=tempData>>8;
	sendBuf[5]=tempData;
	crcData = crc16(sendBuf,6);
	sendBuf[6] = crcData & 0xff;   // CRC代码低位在前
    sendBuf[7] = crcData >> 8 ;	   //高位在后
	receCount = 0; 
	
	if(uartsends(sockfd,sendBuf,8))
	return FAILURE;

	if(uartrcv(sockfd,receBuf,8,TimeOutCount))
	return FAILURE;
	/*
	printf("----------------------\r\n");
	for (int j = 0; j<8; j++)
		printf(" %d :OX%x  ", j, sendBuf[j]);
	printf("\r\n");
	for (int j = 0; j<8; j++)
		printf(" %d :OX%x  ", j, receBuf[j]);
	printf("----------------------\r\n");
	*/
      if((receBuf[0]==addr)&&(receBuf[1]==6)&&(receBuf[6]==sendBuf[6])&&(receBuf[7]==sendBuf[7]))
        {
		receCount = 0;
		  return SUCCESS;
		}									        
	  receCount = 0; 	
	  return FAILURE;
}


/********从机响应主机问询函数，function code : 15，设置多个线圈值 *********/
//////////////询问数据包格式：
///////////////////////// receBuf[0] receBuf[1]  receBuf[2]     receBuf[3]   receBuf[4]    receBuf[5]   receBuf[6]  receBuf[7]  receBuf[8] ... receBuf[9]   receBuf[10] 
//询问数据格式：receBuf[]={从站地址， 功能码，  起始地址高位，起始地址低位，寄存器数高位，寄存器数低位， 字节计数，  数据高位，  数据低位，... 校验码低位， 校验码高位}
/****************************************************************************/
uint8_t presetMultipleCoils(int sockfd,uint8_t addr,uint16_t RegisterStartAddr,uint16_t bitCount)
{
	uint8_t receBuf[50];
	uint8_t sendBuf[50];
	uint16_t crcData,temp,i;
	uint16_t tempData;
	uint16_t byteCount;
	uint16_t  address; //当前寄存器地址
	uint16_t  k;
	uint8_t  exit = 0;
	sendBuf[0]=addr;
	sendBuf[1]=15;
	sendBuf[2]=RegisterStartAddr>>8;
	sendBuf[3]=RegisterStartAddr;	
	sendBuf[4]=bitCount>>8;	   //线圈输出数量
	sendBuf[5]=bitCount;		//000019-Tx:01 0F 00 00 00 0A 02 0C 00 E0 38
	byteCount = bitCount/8;    //计算接收字节个数000005-Tx:01 0F 00 00 00 02 01 00 DE 97
	if(bitCount%8 != 0)
    byteCount++; 			   
	sendBuf[6]=byteCount;  	   //线圈数量对应的字节数
	 address= RegisterStartAddr;
	for(k=0;k<byteCount;k++)//字节位置
  			{
    		for(i=0;i<8;i++)
    			{ 
      			 getCoilVal(address,&tempData);
				 sendBuf[6+i]|=(tempData&0x01)<<i;
      			 address++;
      		    if(address >= RegisterStartAddr+bitCount)//读完
      		       { 
        		    exit = 1;
            		break;
                   } 
    			}
  		 if(exit == 1)
  		  break;
  			}
	crcData = crc16(sendBuf,bitCount+7);
	sendBuf[bitCount+7] = crcData & 0xff;   // CRC代码低位在前
    sendBuf[bitCount+8] = crcData >> 8 ;	   //高位在后
	receCount = 0; 

	if(uartsends(sockfd,sendBuf,bitCount+9))
	return FAILURE;

	if(uartrcv(sockfd,receBuf,8,TimeOutCount))
	return FAILURE; 
	                          //关中断
      if((receBuf[0]==addr)&&(receBuf[1]==15))
        {
		crcData = crc16(receBuf,6);                     //核对校验码
	  temp=receBuf[7];
	  temp=(temp<<8)+receBuf[6];			 //低位在前
      if(crcData == temp)
        {
			receCount = 0; 
		  return SUCCESS;
		}	
		}									       
	  	receCount = 0; 	
	  	return FAILURE;
}

/********从机响应主机问询函数，function code : 16，设置多个寄存器值 *********/
//////////////询问数据包格式：
///////////////////////// receBuf[0] receBuf[1]  receBuf[2]     receBuf[3]   receBuf[4]    receBuf[5]   receBuf[6]  receBuf[7]  receBuf[8] ... receBuf[9]   receBuf[10] 
//询问数据格式：receBuf[]={从站地址， 功能码，  起始地址高位，起始地址低位，寄存器数高位，寄存器数低位， 字节计数，  数据高位，  数据低位，... 校验码低位， 校验码高位}
/****************************************************************************/
uint8_t presetMultipleRegisters(int sockfd,uint8_t addr,uint16_t RegisterStartAddr,uint16_t registerNum)
{
	uint16_t crcData,temp,i;
	uint16_t tempData;
	uint8_t receBuf[50];
	uint8_t sendBuf[50];

	sendBuf[0]=addr;
	sendBuf[1]=16;
	sendBuf[2]=RegisterStartAddr>>8;
	sendBuf[3]=RegisterStartAddr;
	sendBuf[4]=registerNum>>8;
	sendBuf[5]=registerNum;
	sendBuf[6]=registerNum*2;
	for(i=0;i<registerNum;i++)
	{
	getRegisterVal(RegisterStartAddr+i+40000,&tempData);
	 sendBuf[7+2*i]=tempData>>8;
	 sendBuf[8+2*i]=tempData;
	}
	crcData = crc16(sendBuf,registerNum*2+7);
	sendBuf[registerNum*2+7] = crcData & 0xff;   // CRC代码低位在前
    sendBuf[registerNum*2+8] = crcData >> 8 ;	   //高位在后
	receCount = 0; 
	if(uartsends(sockfd,sendBuf,registerNum*2+9))
	return FAILURE;

	if(uartrcv(sockfd,receBuf,8,TimeOutCount))
	return FAILURE; 

     if((receBuf[0]==addr)&&(receBuf[1]==16))
       {
	  crcData = crc16(receBuf,8);                     //核对校验码
	  temp=receBuf[7];
	  temp=(temp<<8)+receBuf[6];			 //低位在前
      if(crcData == temp)
        {
			receCount = 0; 
		  return SUCCESS;
		}	
		}									       
	  	receCount = 0; 	
	  	return FAILURE;
}


	
/*********************************************************************************************/
//                            寄存器数据交换区，根据实际修改

/*******************************读取寄存器内容函数 返回0表示成功**************************/
/********************以下为移植的数据修改区**************************************************/
//取线圈状态 返回0表示成功
static uint16_t getCoilVal(uint16_t addr,uint16_t *tempData)
{
uint16_t tempAddr;
tempAddr = addr;//只取低8位地址
switch(tempAddr)
{
//  case RWSTATUES0:      *tempData = RWstatus0; break;
//  case RWSTATUES1:      *tempData = RWstatus1; break;
//  case ROSTATUES0:      *tempData = ROstatus0; break;  
//  case ROSTATUES1:      *tempData = ROstatus1;   break; 
  default:  break;     
} 
return  SUCCESS;
}

static uint16_t setCoilVal(uint16_t addr,uint16_t tempData)//设定线圈状态 返回0表示成功
{
uint16_t tempAddr;
tempAddr = addr; 
switch(tempAddr)
{
//  case RWSTATUES0:   RWstatus0= tempData; break;
//  case RWSTATUES1:   RWstatus1= tempData; break;
//  case ROSTATUES0:   ROstatus0= tempData; break;  
//  case ROSTATUES1:   ROstatus1= tempData; break;  
  default: break;  
} 
return SUCCESS;
}
	
/*********************************************************************************************/
//                            寄存器数据交换，根据实际修改

/*******************************读取寄存器内容函数 返回0表示成功**************************/
static uint16_t getRegisterVal(uint16_t addr,uint16_t *tempData)
{
	switch(addr)								 
	{
	 case RODAT0: { *tempData = ROdat[0]; break; }
	 case RODAT1: { *tempData = ROdat[1]; break; }
	 case RODAT2: { *tempData = ROdat[2]; break; }
	 case RWDAT0: { *tempData = RWdat[0]; break; }
	 case RWDAT1: { *tempData = RWdat[1]; break; }
	 case RWDAT2: { *tempData = RWdat[2]; break; } 
	 case RWDAT3: { *tempData = RWdat[3]; break; }
	 case RWDAT4: { *tempData = RWdat[4]; break; }
	 case RWDAT5: { *tempData = RWdat[5]; break; } 	 
		
	 default:  break;  
	}
	return SUCCESS;
}


/*******************************设置寄存器内容函数 *返回0表示成功*************************/

static uint16_t setRegisterVal(uint16_t addr,uint16_t tempData)
{
	switch(addr)
	{
	 case RODAT0: { ROdat[0]=tempData ; break; }
	 case RODAT1: { ROdat[1]=tempData ; break; }
	 case RODAT2: { ROdat[2]=tempData  ; break; }    
	 case RWDAT0: { RWdat[0]=tempData  ; break; }  
	 case RWDAT1: { RWdat[1]=tempData  ; break; }  
	 case RWDAT2: { RWdat[2]=tempData  ; break; } 
	 case RWDAT3: { RWdat[3]=tempData  ; break; }  
	 case RWDAT4: { RWdat[4]=tempData  ; break; }  
	 case RWDAT5: { RWdat[5]=tempData  ; break; }  	 
	}
	return SUCCESS;
}
